package org.nutz.dao.impl;

import java.sql.Connection;
import java.sql.DatabaseMetaData;

import javax.sql.DataSource;

import org.nutz.dao.ConnCallback;
import org.nutz.dao.DatabaseMeta;
import org.nutz.dao.SqlManager;
import org.nutz.dao.entity.EntityMaker;
import org.nutz.dao.impl.entity.AnnotationEntityMaker;
import org.nutz.dao.impl.sql.NutPojoMaker;
import org.nutz.dao.impl.sql.run.NutDaoExecutor;
import org.nutz.dao.impl.sql.run.NutDaoRunner;
import org.nutz.dao.jdbc.JdbcExpert;
import org.nutz.dao.jdbc.Jdbcs;
import org.nutz.dao.sql.DaoStatement;
import org.nutz.dao.sql.PojoMaker;
import org.nutz.dao.sql.Sql;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * Dao 接口实现类的一些基础环境
 * 
 * @author zozoh(zozohtnt@gmail.com)
 */
public class DaoSupport {

	private static final Log log = Logs.get();

	/**
	 * 给子类使用的 Dao 的运行器，用来封装事务
	 */
	protected DaoRunner runner;

	/**
	 * 给子类使用的 Dao 语句执行器，用来具体运行某一条语句
	 */
	protected DaoExecutor executor;

	/**
	 * 给子类使用数据源
	 */
	protected DataSource dataSource;

	/**
	 * 给子类使用的数据特殊性的封装
	 */
	protected JdbcExpert expert;

	/**
	 * 给子类使用的 PojoStatementMaker 接口
	 */
	protected PojoMaker pojoMaker;

	/**
	 * 给子类使用的 Entity 获取对象
	 */
	protected EntityHolder holder;

	/**
	 * 数据库的描述
	 */
	private DatabaseMeta meta;

	/**
	 * SQL 管理接口实现类
	 */
	private SqlManager sqlManager;

	public DaoSupport() {
		this.runner = new NutDaoRunner();
		this.executor = new NutDaoExecutor();
	}

	/**
	 * @return Sql 管理接口的实例
	 */
	public SqlManager sqls() {
		return sqlManager;
	}

	/**
	 * @return 当前连接的数据库的一些描述数据
	 */
	public DatabaseMeta meta() {
		return meta;
	}

	/**
	 * 设置一个新的 Sql 管理接口实例
	 * 
	 * @param sqls
	 *            Sql 管理接口实例
	 */
	public void setSqlManager(SqlManager sqls) {
		this.sqlManager = sqls;
	}

	/**
	 * 设置一个新的 Dao 运行器
	 * 
	 * @param runner
	 *            运行器对象
	 */
	public void setRunner(DaoRunner runner) {
		this.runner = runner;
	}

	/**
	 * 设置一个新的 Dao 语句执行器
	 * 
	 * @param executor
	 *            Dao 语句执行器对象
	 */
	public void setExecutor(DaoExecutor executor) {
		this.executor = executor;
	}

	/**
	 * 设置一个新的 Pojo 语句创建器
	 * 
	 * @param pojoMaker
	 *            Pojo 语句创建器
	 */
	public void setPojoMaker(PojoMaker pojoMaker) {
		this.pojoMaker = pojoMaker;
	}

	/**
	 * @return 当前的 JDBC 专家类
	 */
	public JdbcExpert getJdbcExpert() {
		return expert;
	}

	/**
	 * 设置新的数据源。
	 * <p>
	 * 如果有老的数据源需要你在外部手动关闭
	 * 
	 * @param ds
	 *            数据源
	 */
	public void setDataSource(DataSource ds) {
		if (null != dataSource)
			if (log.isWarnEnabled())
				log.warn("Replaced a running dataSource!");
		dataSource = ds;
		expert = Jdbcs.getExpert(ds);
		pojoMaker = new NutPojoMaker(expert);

		meta = new DatabaseMeta();
		runner.run(dataSource, new ConnCallback() {
			public void invoke(Connection conn) throws Exception {
				DatabaseMetaData dmd = conn.getMetaData();
				meta.setProductName(dmd.getDatabaseProductName());
				meta.setVersion(dmd.getDatabaseProductVersion());
			}
		});

		holder = new EntityHolder(this);
		holder.maker = createEntityMaker();
	}

	public void execute(final Sql... sqls) {
		_exec(sqls);
	}

	public void run(ConnCallback callback) {
		runner.run(dataSource, callback);
	}

	protected int _exec(final DaoStatement... sts) {
		final int[] re = new int[1];
		runner.run(dataSource, new ConnCallback() {
			public void invoke(Connection conn) throws Exception {
				for (DaoStatement st : sts) {
					executor.exec(conn, st);
					re[0] += st.getUpdateCount();
				}
			}
		});
		return re[0];
	}

	/**
	 * 子类可以重写这个类，用来扩展成其他的实体配置方式
	 * 
	 * @param ds
	 *            数据源
	 * @param expert
	 *            数据源专家类
	 * @return 实体工厂
	 */
	protected EntityMaker createEntityMaker() {
		return new AnnotationEntityMaker(dataSource, expert, holder);
	}

}
